<?php

abstract class Nutri_Model_Abstract
{
    /**
     * @var     int
     */
    protected $_id;
    /**
     * @var     Nutri_Model_Mapper_Abstract
     */
    protected $_mapper;
    
    /**
     * Constructor method for this data model to populate the model with given
     * data elements.
     * 
     * @param   array|Zend_Db_Table_Row $params
     */
    public function __construct($params = null)
    {
        if (null !== $params) {
            $this->populate($params);
        }
    }
    
	public function __set($name, $value)
    {
        $method = 'set' . ucfirst($name);
        if (!method_exists($this, $method)) {
        	require_once 'Zend/Exception.php';
            throw new Zend_Exception('Invalid property');
        }
        $this->$method($value);
    }
 
    public function __get($name)
    {
        $method = 'get' . ucfirst($name);
        if (!method_exists($this, $method)) {
        	require_once 'Zend/Exception.php';
            throw new Zend_Exception('Invalid property');
        }
        return $this->$method();
    }
    
    public function setId($id)
    {
        $this->_id = (int) $id;
        return $this;
    }
    public function getId()
    {
        return $this->_id;
    }
    
    /**
     * Sets the data mapper class for this model
     * 
     * @param   string|Nutri_Model_Mapper_Abstract $mapper
     * @throws  Zend_Exception (for testing purposes only)
     */
    public function setMapper($mapper)
    {
        if (is_string($mapper)) {
            if (!class_exists($mapper)) {
                require_once 'Zend/Exception.php';
                throw new Zend_Exception('Non-existing mapper class provided');
            }
            $mapper = new $mapper;
        }
        if (!$mapper instanceof Nutri_Model_Mapper_Abstract) {
            require_once 'Zend/Exception.php';
            throw new Zend_Exception('Invalid mapper class provided');
        }
        $this->_mapper = $mapper;
    }
    
    /**
     * Method to retrieve the mapper instance so it can be used within our data
     * model without having to instantiate it.
     * 
     * @return  Nutri_Model_Mapper_Abstract
     */
    abstract public function getMapper();
    
    /**
     * Populate this data model with given data elements
     * 
     * @param   array|Zend_Db_Table_Row $row
     * @return  Nutri_Model_Abstract
     */
 	public function populate($row)
    {
        if (is_array($row)) {
            $row = new ArrayObject($row, ArrayObject::ARRAY_AS_PROPS);
        }

    	$methods = get_class_methods($this);
        foreach ($row as $key => $value) {
            $method = 'set' . ucfirst($key);
            if (in_array($method, $methods)) {
                $this->$method($value);
            }
        }
        return $this;
    }
    
    /**
     * Converts this model into an array for storage or rendering purposes
     * 
     * @uses required for sub-class
     * 
     * @return  array
     */
    abstract public function toArray();
    
    /**
     * Finds a single data set matching given primary key ID
     * 
     * @uses required for sub-class
     * 
     * @param   int| $id
     */
    public function find($id)
    {
    	$this->getMapper()->find($this, $id);
    	return $this;
    }
    
    /**
     * Fetch all entries matching given conditions
     * 
     * @param   string|array|null $where
     * @param   string|array|null $order
     * @param   int|null $count
     * @param   int|null $offset
     * @return  array
     */
    public function fetchAll($where = null, $order = null, $count = null, $offset = null)
    {
        $className = get_class($this);
        return $this->getMapper()->fetchAll($className, $where, $order, $count, $offset);
    }
    
	/**
     * Fetch all active entries matching given conditions
     * 
     * @param   string|array|null $order
     * @param   int|null $count
     * @param   int|null $offset
     * @return  array
     */
    public function fetchAllActive($where = null, $order = null, $count = null, $offset = null)
    {
    	if (!empty($where)) {
    		$where = 'active = 1 and ' . $where;
    	} else {
    		$where = 'active = 1';
    	}
    	
        return $this->fetchAll($where, $order, $count, $offset);
    }
    
    /**
     * Find an single instance of this model matching given conditions
     * 
     * @param   string|array|null $where
     * @param   string|array|null $order
     */
    public function fetchRow($where = null, $order = null)
    {
        $this->getMapper()->fetchRow($this, $where, $order);
        return $this;
    }
    
    /**
     * Saves this model to the datastorage
     * 
     * @param   string $primaryKey
     * @return  null|int The last sequence or affected rows by this action
     */
    public function save($primaryKey = 'id')
    {
        return $this->getMapper()->save($this, $primaryKey);
    }
    
	/**
     * Delete this mode to the datastorage
     * 
     * @param   string $primaryKey
     * @return  null|int The last sequence or affected rows by this action
     */
    public function delete($primaryKey = 'id')
    {
        return $this->getMapper()->delete($this, $primaryKey);
    }
    
    /**
     * 
     *  Returns an instance of a Zend_Db_Table_Select object.
     *  @return Zend_Db_Table_Select
     */
	public function select()
    {
        return $this->getMapper()->select();
    }
    
    /**
     * Convert a given value to aModel if is needed
     * @param string $className
     * @param Nutri_Model_Abstract|int $value
     */
    protected function _convertToModel($className, &$value){

    	if (!class_exists($className)){
    		require_once 'Zend/Exception.php';
        	throw new Zend_Exception('class provided not exists');
    	}
    	if ((!$value instanceof $className) && 0 < (int)$value){
    		$valueObj = new $className();
    		$valueObj->find((int)$value);
    		$value = $valueObj;
    	} else if ($value instanceof $className) {
    		$value->find($value->getId());
    	}
    }
}